/*
 * Arbol.cpp
 *
 *  Created on: 03/04/2010
 *      Author: administrador
 */

#include "Arbol.h"
#include <iostream>
#include <fstream>
#include "Excepciones.h"
#include "FactoryNodo.h"
#include "Serializable.h"

Arbol* Arbol::instancia = NULL;
int Arbol::tamanioMaximoNodo = 0;
int Arbol::idRaiz = 0;
const float Arbol::porcentajeCargaMaxima = 0.5;
const float Arbol::porcentajeOverflow = 0.8;
const float Arbol::porcentajeUnderflow = 0.35;

Arbol::Arbol(int tamanioMaximo){

	Archivo::setTamanioBloque(tamanioMaximo);

	setTamanioMaximoNodo(tamanioMaximo);

	Archivo::setCantidadMaximaBloques(100000);

	if (Archivo::getInstance("r+")){

		this->hidratar();
	}
	else{

		Archivo::getInstance()->getBloqueLibre();

		this -> cantidadNiveles = 1;

		this->raiz = FactoryNodo::factory(this->cantidadNiveles);

		idRaiz = raiz->getId();

		this -> nodoActual = NULL;

		this -> elementoActual = NULL;

	}

	// TODO: Crear archivo de referencias a nodos libres vacio
}

Arbol* Arbol::getInstancia() {
	if (instancia == NULL) {

		instancia = new Arbol(tamanioMaximoNodo);
	}
	return instancia;
}

void Arbol::borrarInstancia() {
	delete instancia;
	instancia = NULL;
}

int Arbol::getIdRaiz(){

	return idRaiz;
}

void Arbol::setTamanioMaximoNodo(int tamanio)
{
	tamanioMaximoNodo = tamanio;
	INodo::setTamanioNodo(tamanio);
	Archivo::setTamanioBloque(tamanio);
}

Arbol::~Arbol() {

	this->raiz->serializar();

	this->serializar();

	delete this->raiz;

	if (this->elementoActual){
		delete this->elementoActual;
	}

	if (this->nodoActual){

		delete this->nodoActual;
	}
	Archivo::getInstance()->deleteInstance();

}


void Arbol::solucionarOverflow() {
	INodo* nodoHijoIzquierdo;

	INodo* nodoHijoDerecho;

	nodoHijoDerecho = FactoryNodo::factory(this->cantidadNiveles);

	INodo *nuevaRaiz = FactoryNodo::factory(this->cantidadNiveles + 1);

	int idNodoHijoIzquierdo = nuevaRaiz -> getId();

	nuevaRaiz -> setId(idRaiz);

	nodoHijoIzquierdo = this -> raiz;

	nodoHijoIzquierdo -> setId(idNodoHijoIzquierdo);

	this -> raiz = nuevaRaiz;

	Clave claveAPromover;

	nodoHijoIzquierdo -> partirNodoEnOverflow(claveAPromover, nodoHijoDerecho);

	((NodoIndice*) this->raiz)->agregarClaveElemento(claveAPromover);

	((NodoIndice*) this->raiz)->agregarIdNodo(idNodoHijoIzquierdo);

	((NodoIndice*) this->raiz)->agregarIdNodo(nodoHijoDerecho->getId());

	nodoHijoIzquierdo->serializar();

	nodoHijoDerecho->serializar();

	delete nodoHijoIzquierdo;

	delete nodoHijoDerecho;

	/* Se suma un nivel al arbol */
	cantidadNiveles++;

}

void Arbol::solucionarUnderflow() {
	int  idNuevaRaiz;
	INodo* nuevaRaiz;

	/* Si la cantidad de niveles es mayor que uno, la raiz es indice, y si tiene solo un hijo entonces la raiz nueva
	 * es el unico hijo de la raiz original */

	if ((cantidadNiveles > 1) && (((NodoIndice*) raiz) -> getCantidadElementos() == 0)) {

		INodo *raizVieja = this->raiz;
		idNuevaRaiz = ((NodoIndice*) raiz)->getIdNodo(0);
		nuevaRaiz = FactoryNodo::factory(this->cantidadNiveles-1,idNuevaRaiz);
		raiz = nuevaRaiz;
		raiz->setId(idRaiz);
		delete raizVieja;
		cantidadNiveles--;
		// TODO: No se si hay que deletear la raiz original!!!
	}

	if ((cantidadNiveles==1)&& (raiz->getCantidadElementos()==0)){

		this->raiz->vaciarNodo();

	}
}

void Arbol::setNombreArchivo(char* nombre){

	Archivo::setNombre(nombre);
}


void Arbol::insertarElemento(Elemento* elemento)
{
	setElementoActual(NULL);
	
	setNodoActual(NULL);

	if (elemento -> getTamanio() < (tamanioMaximoNodo * porcentajeCargaMaxima))
	{
		try
		{
			if (raiz -> insertarElemento(elemento))
			{
				raiz -> serializar();
			}
		}
		catch (ElementoExistenteException ex)
		{
			ex.mostrarError();
		}
		catch (OverflowException ex)
		{
			solucionarOverflow();

			raiz -> serializar();
		}
	}
	else
	{
		cout<<"Elemento de tamaño mayor a la mitad del tamaño maximo de nodo"<<endl;
	}
}

void Arbol::quitarElemento(Clave clave) {

	setElementoActual(NULL);

	setNodoActual(NULL);

	try
	{
		if (raiz -> quitarElemento(clave))
		{
			raiz -> serializar();
			if (elementoActual!=NULL){

				delete elementoActual;

				elementoActual=NULL;

				if (nodoActual!=NULL){

					delete nodoActual;

					nodoActual=NULL;
				}
			}
		}
	}
	catch (ElementoInexistenteException ex)
	{
		ex.mostrarError();
	}
	catch (UnderflowException ex)
	{
		solucionarUnderflow();

		raiz -> serializar();
	}

}

void Arbol::modificarElemento(Clave clave, string* nuevoValor)
{
	setElementoActual(NULL);
	
	setNodoActual(NULL);

	int tamanioNuevoElemento = sizeof(clave) + sizeof(int) + (sizeof(char)*nuevoValor->length());

	if (tamanioNuevoElemento < (tamanioMaximoNodo * porcentajeCargaMaxima))
	{
		try
		{
			if (raiz -> modificarElemento(clave, nuevoValor))
			{
				raiz -> serializar();
			}
		}
		catch (NodoVacioException ex)
		{
			ex.mostrarError();
		}
		catch (ElementoInexistenteException ex)
		{
			ex.mostrarError();
		}
		catch (OverflowException ex)
		{
//			ex.mostrarError();

			solucionarOverflow();

			raiz -> serializar();
		}
		catch (UnderflowException ex)
		{
//			ex.mostrarError();

			solucionarUnderflow();

			raiz -> serializar();
		}
	}
	else
	{
		cout<<"Valor de tamaño mayor a la mitad del tamaño maximo de nodo"<<endl;
	}
}

Elemento* Arbol::buscarElemento(Clave clave) {
	Elemento* aBuscar = NULL;
	Elemento* aDevolver = NULL;

	try
	{
		aBuscar = raiz -> buscarElemento(clave);
		aDevolver = aBuscar?aBuscar->clonar():NULL;
	}
	catch (NodoVacioException ex)
	{
		ex.mostrarError();
	}
	catch (ElementoInexistenteException ex)
	{
//		cout<<"El Elemento "<<ex.getElemento().getClave()<<" no existe ni exite un elemento con clave mayor"<<endl;
	}

	return aDevolver;
}

//Elemento* Arbol::primerElemento() {
//	return this -> buscarElemento(0);
//}

void Arbol::setNodoActual(NodoHoja* nodo)
{
	if (this->nodoActual!=NULL){

		delete this -> nodoActual;
	}

	if (nodo!=NULL){

		this -> nodoActual = nodo -> clonar();
	}
	else{

		this->nodoActual = NULL;
	}
}

Elemento* Arbol::getElementoActual()
{
	if (elementoActual == NULL)
	{
		return NULL;
	}
	else
	{
		return this -> elementoActual;
	}
}

void Arbol::setElementoActual(Elemento* elemento)
{
	if (this->elementoActual != NULL){
		
		delete this -> elementoActual;
	}

	if (elemento!=NULL){
		
		this -> elementoActual =  elemento -> clonar();
	}
	else{
		
		this->elementoActual = NULL;
	}
}

Elemento* Arbol::getElementoContiguo(int desplazamiento)
{
	if (elementoActual == NULL)
	{
		return NULL;
	}
	else
	{
		Elemento* aRetornar = NULL;

		try
		{
			aRetornar = nodoActual -> getElementoContiguo(desplazamiento);
		}
		catch (NodoVacioException ex)
		{
			ex.mostrarError();
		}
		catch (ElementoInexistenteException ex)
		{
//			cout<<"El elemento "<<this -> elementoActual -> getClave().getClave()<<" no tiene siguiente"<<endl;
		}

		return aRetornar;
	}
}

void Arbol::print()
{
	ofstream archivoSalida("ListadoArbol.txt");

	archivoSalida << "Tamanio Bloque: " << tamanioMaximoNodo << " Factor overflow: " << tamanioMaximoNodo*porcentajeOverflow << " Factor underflow " << tamanioMaximoNodo*porcentajeUnderflow << " Cantidad de Niveles " << this->cantidadNiveles << "\n\n";
	this->raiz->printNodo(&archivoSalida,this->cantidadNiveles);

	archivoSalida.close();

}

void Arbol::hidratar(){

	c_bloque  *controlArchivo = new c_bloque();

	controlArchivo->set_nro(0);

	int hayElementoActual;

	Clave claveElementoActual;

	int idNodoActual;

	controlArchivo->load();

	controlArchivo->get(&(this->idRaiz));

	controlArchivo->get((&this->cantidadNiveles));

	controlArchivo->get(&hayElementoActual);

	if (hayElementoActual==1){

		Clave claveElementoActual;

		claveElementoActual.hidratar(controlArchivo);

		controlArchivo->get(&idNodoActual);

		this->nodoActual = new NodoHoja(idNodoActual);

		this->nodoActual->hidratar();

		for (int i=0; i<this->nodoActual->getCantidadElementos(); i++){

			if (claveElementoActual == this->nodoActual->getElemento(i)->getClave()){

				this->elementoActual = this->nodoActual->getElemento(i)->clonar();
				break;
			}
		}
	}
	else{

		this->elementoActual =NULL;
		this->nodoActual = NULL;
	}

	if (this->idRaiz!=0){
		this->raiz = FactoryNodo::factory(this->cantidadNiveles,this->idRaiz);

	}
	else{

		this->raiz = new NodoHoja();
		this->raiz->hidratar();
		this->idRaiz = this->raiz->getId();
	}

	delete controlArchivo;

}

void Arbol::serializar(){

	c_bloque *controlArchivo = new c_bloque();

	controlArchivo->set_nro(0);

	if (raiz->getCantidadElementos()==0){
		this->idRaiz =0;
	}

	controlArchivo->add(this->idRaiz);

	controlArchivo->add(this->cantidadNiveles);

	int hayElementoActual=0;

	if (this->elementoActual!=NULL){

		hayElementoActual = 1;

		controlArchivo->add(hayElementoActual);

		this->elementoActual->getClave().serializar(controlArchivo);

		int idNodoActual = this->nodoActual->getId();

		controlArchivo->add(idNodoActual);
	}
	else{

		controlArchivo->add(hayElementoActual);
	}

	controlArchivo->save();

	delete controlArchivo;

}

